package org.ring.csr.logger;

import java.io.OutputStream;
import java.io.PrintWriter;

import com.puppycrawl.tools.checkstyle.api.AuditEvent;
import com.puppycrawl.tools.checkstyle.api.AuditListener;
import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
import com.puppycrawl.tools.checkstyle.api.SeverityLevel;

/**
 * 
 * @author Ray Ling
 */
public class MessageLogger extends AutomaticBean implements AuditListener {
    /** where to write info messages **/
    private final PrintWriter mInfoWriter;
    /** close info stream after use */
    private final boolean mCloseInfo;

    /** where to write error messages **/
    private final PrintWriter mErrorWriter;
    /** close error stream after use */
    private final boolean mCloseError;

    /** where to write error messages **/
    private final PrintWriter mWarningWriter;
    /** close error stream after use */
    private final boolean mCloseWarning;

    /** where to write error messages **/
    private final PrintWriter mExceptionWriter;
    /** close error stream after use */
    private final boolean mCloseException;

    public MessageLogger() {
	this(null, false, null, false, null, false, null, false);
    }

    public MessageLogger(OutputStream aErrorStream,
	    boolean aCloseStreamsAfterUse) {
	// no need to close aOS twice
	this(aErrorStream, aCloseStreamsAfterUse, null, false, null, false,
		null, false);
    }

    public MessageLogger(OutputStream aErrorStream,
	    boolean aCloseErrorAfterUse, OutputStream aWarningStream,
	    boolean aCloseWarningAfterUse) {
	this(aErrorStream, aCloseErrorAfterUse, aWarningStream,
		aCloseWarningAfterUse, null, false, null, false);
    }

    public MessageLogger(OutputStream aErrorStream,
	    boolean aCloseErrorAfterUse, OutputStream aWarningStream,
	    boolean aCloseWarningAfterUse, OutputStream aInfoStream,
	    boolean aCloseInfoAfterUse) {
	this(aErrorStream, aCloseErrorAfterUse, aWarningStream,
		aCloseWarningAfterUse, aInfoStream, aCloseInfoAfterUse, null,
		false);
    }

    public MessageLogger(OutputStream aErrorStream,
	    boolean aCloseErrorAfterUse, OutputStream aWarningStream,
	    boolean aCloseWarningAfterUse, OutputStream aInfoStream,
	    boolean aCloseInfoAfterUse, OutputStream aExceptionStream,
	    boolean aCloseExceptionAfterUse) {
	mCloseError = aCloseErrorAfterUse;
	mCloseWarning = aCloseWarningAfterUse;
	mCloseInfo = aCloseInfoAfterUse;
	mCloseException = aCloseExceptionAfterUse;

	mErrorWriter = (null == aErrorStream) ? null : new PrintWriter(
		aErrorStream);
	mWarningWriter = (null == aWarningStream) ? null : new PrintWriter(
		aWarningStream);
	mInfoWriter = (null == aInfoStream) ? null : new PrintWriter(
		aInfoStream);
	mExceptionWriter = (null == aExceptionStream) ? null : new PrintWriter(
		aExceptionStream);
    }

    public final void addError(AuditEvent aEvt) {
	final SeverityLevel severityLevel = aEvt.getSeverityLevel();
	if (!SeverityLevel.IGNORE.equals(severityLevel)) {

	    switch (severityLevel) {
	    case INFO:
		if (null != mInfoWriter) {
		    mInfoWriter.println(String.format("Info:(%s,%s) in %s: %s",
			    aEvt.getLine(), aEvt.getColumn(),
			    aEvt.getFileName(), aEvt.getMessage()));
		}
		break;
	    case WARNING:
		if (null != mWarningWriter) {
		    mWarningWriter.println(String.format(
			    "Warning:(%s,%s) in %s: %s", aEvt.getLine(),
			    aEvt.getColumn(), aEvt.getFileName(),
			    aEvt.getMessage()));
		}
		break;
	    case ERROR:
		if (null != mErrorWriter) {
		    mErrorWriter.println(String.format(
			    "Error:(%s,%s) in %s: %s", aEvt.getLine(),
			    aEvt.getColumn(), aEvt.getFileName(),
			    aEvt.getMessage()));
		}
		break;
	    default:
		break;
	    }
	}
    }

    /** {@inheritDoc} */
    public final void addException(AuditEvent aEvt, Throwable aThrowable) {
	if (null != mExceptionWriter) {
	    synchronized (mErrorWriter) {
		mExceptionWriter
			.println("Error auditing " + aEvt.getFileName());
		aThrowable.printStackTrace(mErrorWriter);
	    }
	}
    }

    /** {@inheritDoc} */
    public final void auditStarted(AuditEvent aEvt) {
	if (null != mInfoWriter) {
	    mInfoWriter.println("Starting audit...");
	}
    }

    /** {@inheritDoc} */
    public void fileFinished(AuditEvent aEvt) {
    }

    /** {@inheritDoc} */
    public void fileStarted(AuditEvent aEvt) {
    }

    /** {@inheritDoc} */
    public final void auditFinished(AuditEvent aEvt) {
	if (null != mInfoWriter) {
	    mInfoWriter.println("Audit done.");
	}
	closeStreams();
    }

    /**
     * Flushes the output streams and closes them if needed.
     */
    protected final void closeStreams() {
	if (null != mErrorWriter) {
	    mErrorWriter.flush();
	    if (mCloseError) {
		mErrorWriter.close();
	    }
	}

	if (null != mWarningWriter) {
	    mWarningWriter.flush();
	    if (mCloseWarning) {
		mWarningWriter.close();
	    }
	}

	if (null != mInfoWriter) {
	    mInfoWriter.flush();
	    if (mCloseInfo) {
		mInfoWriter.close();
	    }
	}

	if (null != mExceptionWriter) {
	    mExceptionWriter.flush();
	    if (mCloseException) {
		mExceptionWriter.close();
	    }
	}
    }
}
